package com.shiro.tqw.shiro1;

import java.util.Arrays;


import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.authz.permission.WildcardPermissionResolver;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.druid.pool.DruidDataSource;
import com.shiro1.realm.MyJdbcRealm;

public class testJDBCRealm {

	private Logger logger;

	@Before
	public void beforeHelloword() {
		logger = LoggerFactory.getLogger(testJDBCRealm.class);		
	}

	/**
	 * 1:先测试通过 ini配置文件，指定的 JdbcRealm的情况
	 * 2:第一步先根据shiro.sql创建数据库和表[在src/test/resource/sql目录下]
	 * 3:根据自己的mysql数据库的用户名密码修改相关数据源信息。
	 * 4:通过查看 org.apache.shiro.realm.jdbc.JdbcRealm的源码可以看到如下sql语句:
	 *   DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
	 *   DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";
	 *   DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
	 *   DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
	 *   可以看出shiro默认的jdbcRealm的实现所访问的数据库表结构。与shiro.sql的语句创建的一样
	 * 5:在我们的shiro.sql脚本中，最后一句我们插入了一个用户 zhang.其密码是123.
	 *   现在我们获取的realm是从数据库中获取的 
	 * 6:现在的问题是：虽然我们可以从数据库中获取realm了，但是这个数据库的表，与我们自己用的用户权限表，并不相一致。
	 * 	 怎么样才能让shiro的权限控制与每个人自己的权限数据库相结合呢？
	 *  方法1:可以自己写一个jdbcrealm.然后让securityManager的Realm指定我们的Realm即可。
	 *       具体参考MyJdbcRealm和shiro默认的JdbcRealm.
	 * 	方法2:在realm验证时自己去数据库获取。并验证，再返回验证信息??????
	 * 7：先让我们学习一下纯代码版的jdbcRealm测试
	 */
	@Test
	public void testIniJdbcReam() {
		Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini");
		SecurityManager securityManager = factory.getInstance();
		SecurityUtils.setSecurityManager(securityManager);

		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
		try {
			// 4、登录，即身份验证
			subject.login(token);
		} catch (AuthenticationException e) {
			// 5、身份验证失败
			e.printStackTrace();
		}

		Assert.assertEquals(true, subject.isAuthenticated()); // 断言用户已经登录
		subject.logout();
		logger.info("testIniJdbcReam end :" + token);
	}
	
	/**
	 * 非配置文件的验证写法
	 * 
	 * Administrator
	 * 2016年7月29日
	 */
	@Test
	public void testNonConfigJdbcRealm(){
		//获取默认的安全管理器
		DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //设置authenticator及认证的策略，致少有一个成功
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        securityManager.setAuthenticator(authenticator);

        //设置authorizer,设置授权解释器，通配符权限解释器。意思是支持*.具体的规则要看其中的内容
        ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer();
        authorizer.setPermissionResolver(new WildcardPermissionResolver());
        securityManager.setAuthorizer(authorizer);

        //设置Realm
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl("jdbc:mysql://localhost:3306/shiro");
		datasource.setDriverClassName("com.mysql.jdbc.Driver");
		datasource.setUsername("root");
		datasource.setPassword("root");
		//设置jdbcRealm
        JdbcRealm jdbcRealm = new JdbcRealm();
        
        jdbcRealm.setDataSource(datasource);
        jdbcRealm.setPermissionsLookupEnabled(true);
        securityManager.setRealms(Arrays.asList((Realm) jdbcRealm));

        //将SecurityManager设置到SecurityUtils 方便全局使用
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
        subject.login(token);

        Assert.assertTrue(subject.isAuthenticated());
        subject.logout();
        logger.info("testNonConfigJdbcRealm end :" + token);
	}
	
	/**
	 * 此方法修改了JdbcRealm为自己的MyJdbcRealm。示例仅改了表名。实际使用时
	 * 可根据自己的数据库表做修改
	 */
	@Test
	public void testNonConfigMyJdbcRealm(){
		//获取默认的安全管理器
		DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //设置authenticator及认证的策略，致少有一个成功
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        securityManager.setAuthenticator(authenticator);

        //设置authorizer,设置授权解释器，通配符权限解释器。意思是支持*.具体的规则要看其中的内容
        ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer();
        authorizer.setPermissionResolver(new WildcardPermissionResolver());
        securityManager.setAuthorizer(authorizer);

        //设置Realm
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl("jdbc:mysql://localhost:3306/shiro");
		datasource.setDriverClassName("com.mysql.jdbc.Driver");
		datasource.setUsername("root");
		datasource.setPassword("root");
		//设置jdbcRealm
        MyJdbcRealm jdbcRealm = new MyJdbcRealm();
        
        jdbcRealm.setDataSource(datasource);
        jdbcRealm.setPermissionsLookupEnabled(true);
        securityManager.setRealms(Arrays.asList((Realm) jdbcRealm));

        //将SecurityManager设置到SecurityUtils 方便全局使用
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("wang", "123456");
        subject.login(token);

        Assert.assertTrue(subject.isAuthenticated());
        subject.logout();
        logger.info("testNonConfigMyJdbcRealm end :" + token);
	}
}
